package com.ouyunc.email.config.local.template;


import com.ouyunc.email.config.EmailTemplate;
import com.ouyunc.email.config.local.bo.EmailParam;
import com.ouyunc.email.config.local.enums.EmailEnum;
import com.ouyunc.email.config.local.properties.EmailProperties;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.activation.DataHandler;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.util.ByteArrayDataSource;
import java.util.Date;
import java.util.Properties;

/**
 * @Author fangzhenxun
 * @Description email 模板的具体实现类
 * @Date 2020/3/16 16:36
 **/
@Component
public class LocalEmailTemplate implements EmailTemplate {
    private static Logger log = LoggerFactory.getLogger(EmailTemplate.class);

    /**
     * mime子混合类型
     **/
    private static final String SUBTYPE_MIXED = "mixed";

    /**
     * mime子联系类型
     **/
    private static final String SUBTYPE_RELATED = "related";

    /**
     * content_type 类型
     **/
    private static final String CONTENT_TYPE_TEXT_HTML = "text/html;charset=UTF-8";

    /**
     * mime 类型
     **/
    private static final String MIME_APPLICATION_OCTET_STREAM = "application/octet-stream";


    /**
     * 邮件配置相关属行
     **/
    @Autowired
    private EmailProperties emailProperties;

    /**
     * @param emailParam 封装了发送邮件的参数（如接收人，抄送人，发送内容 等）
     * @return void
     * @Author fangzhenxun
     * @Description 发送邮件(异步) 注意后期可以自己配置线程池来确定
     * @Date 2020/3/16 16:37
     **/
    @Override
    public void sendMail(EmailParam emailParam) {
        //参数配置
        Properties props = new Properties();
        //邮箱协议
        props.setProperty(EmailEnum.PROTOCOL.getValue(), emailProperties.getEmailProtocol());
        //发件人的邮箱的 SMTP 服务器地址(域名地址)
        props.setProperty(EmailEnum.HOST.getValue(), emailProperties.getEmailHost());
        //是否开启邮箱授权
        props.setProperty(EmailEnum.AUTH.getValue(), emailProperties.getEmailAuth());
        //创建定义整个应用程序所需的环境信息的 Session 对象
        Session session = Session.getInstance(props, new Authenticator() {
            //发件人邮件地址、授权码
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(emailProperties.getFromAddress(), emailProperties.getAuthorizationCode());
            }
        });

        //开启Session的debug模式，这样就可以查看到程序发送Email的运行状态
        session.setDebug(true);
        //构造邮件消息
        Message message = new MimeMessage(session);

        try {
            //指明邮件的发件人,并拼接发件人昵称（如果有）
            String emailNick = MimeUtility.encodeText(emailProperties.getEmailNick());
            message.setFrom(new InternetAddress(emailNick + "<" + emailProperties.getFromAddress() + ">"));
            //指明邮件的收件人,多个以英文逗号隔开
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(emailParam.getToAddress()));
            //回复到的邮件地址
            message.setReplyTo(InternetAddress.parse(emailProperties.getReplayAddress()));
            // 设置抄送人
            if (StringUtils.isNoneBlank(emailParam.getCcAddress())) {
                message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(emailParam.getCcAddress()));
            }
            // 设置密送人
            if (StringUtils.isNoneBlank(emailParam.getBccAddress())) {
                message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(emailParam.getBccAddress()));
            }
            // 设置邮件标题
            if (StringUtils.isNoneBlank(emailParam.getSubject())) {
                message.setSubject(emailParam.getSubject());
            }

            //==============================================下面是邮件内容的创建=========================================================
            //1,创建全局MimeMultipart根节点,subtype默认类型是mixed混合类型
            MimeMultipart rootMultipart = new MimeMultipart(SUBTYPE_MIXED);

            //2,创建（文本+图片）MimeMultipart节点，subtype类型设置related关联关系
            MimeMultipart textImageMultipart = new MimeMultipart(SUBTYPE_RELATED);

            //3,创建文本body体
            MimeBodyPart textBodyPart = new MimeBodyPart();
            //3.1,设置文本,注意当文本中有html片段时且包含<img >标签使用cid来引用图片作为文件内容时，存在两种情况：（1）如果cid不存在则，在邮件内容区不会显示该图片，该图片没有被消费会以附件的方式存在邮件中；（2）若cid与设置的图片id则会消费该图片（可重复消费）不会在附件中显示
            textBodyPart.setContent(emailParam.getContent() == null ? "" : emailParam.getContent(), CONTENT_TYPE_TEXT_HTML);
            //3.2,将内容文本添加到（文本+图片）MimeMultipart节点中
            textImageMultipart.addBodyPart(textBodyPart);


            //4,创建图片内容体(可能有多个使用循环添加)
            if (MapUtils.isNotEmpty(emailParam.getImageFileMap())) {
                emailParam.getImageFileMap().forEach((fileName, imageFileByte) -> {
                    try {
                        MimeBodyPart imageBodyPart = new MimeBodyPart();
                        //4.1，创建数据处理器
                        DataHandler imageDataHandler = new DataHandler(new ByteArrayDataSource(imageFileByte, MIME_APPLICATION_OCTET_STREAM));
                        imageBodyPart.setDataHandler(imageDataHandler);
                        //4.2，为“节点”设置一个唯一编号（在文本“节点”将引用该ID）
                        imageBodyPart.setContentID(fileName);
                        //4.3，设置图片名称以完整格式命名（若内容中没有消费则会在附件中完整显示）
                        imageBodyPart.setFileName(MimeUtility.encodeText(fileName));
                        //4.4，将内容图片添加到（文本+图片）MimeMultipart节点中
                        textImageMultipart.addBodyPart(imageBodyPart);
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("创建内容图片错误！");
                    }
                });
            }

            //5,将 文本+图片 的混合“节点”封装成一个普通“节点”,最终添加到邮件的 Content 是由多个 BodyPart 组成的 Multipart, 所以我们需要的是 BodyPart
            MimeBodyPart textImageBodyPart = new MimeBodyPart();
            textImageBodyPart.setContent(textImageMultipart);
            //5.1将（文本+图片）的bodyPart添加到rootMultipart根节点中
            rootMultipart.addBodyPart(textImageBodyPart);


            //6,创建附件体节点体(可能有多个使用循环添加)
            if (MapUtils.isNotEmpty(emailParam.getAttachmentFileMap())) {
                emailParam.getAttachmentFileMap().forEach((fileName, attachmentFileBate) -> {
                    try {
                        MimeBodyPart attachmentBodyPart = new MimeBodyPart();
                        //6.1创建附件处理器
                        DataHandler attachmentDataHandler = new DataHandler(new ByteArrayDataSource(attachmentFileBate, MIME_APPLICATION_OCTET_STREAM));
                        attachmentBodyPart.setDataHandler(attachmentDataHandler);
                        //6.2，为“节点”设置一个唯一编号（在文本“节点”将引用该ID）
                        attachmentBodyPart.setContentID(fileName);
                        //6.3，设置图片名称以完整格式命名（若内容中没有消费则会在附件中完整显示）
                        attachmentBodyPart.setFileName(MimeUtility.encodeText(fileName));
                        //6.4，将附件添加到根节点中
                        rootMultipart.addBodyPart(attachmentBodyPart);
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("创建附件错误！");
                    }
                });
            }


            //设置消息的root
            message.setContent(rootMultipart);
            //设置发件时间
            message.setSentDate(new Date());
            //保存邮件
            message.saveChanges();
            //发送邮件
            Transport.send(message);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("发送邮件异常！ 邮件序列号: {}", emailParam.getEmailSerialNumber());
        }
    }
}
